package visit

import (
	"io/fs"

	"gitee.com/mkwchecking/sr-admin/pkg/file/utils"
)

type VisitCallback func(path string, info FileInfo) error

type IFolderVisitor interface {

	// VisitFolder 遍历文件夹
	// :path 文件夹路径
	// :cb 回调函数
	VisitFolders(paths []string, cb VisitCallback, opts ...Option) error
}

var _ IFolderVisitor = (*Visitor)(nil)

func NewVisitor(opts ...Option) *Visitor {
	opt := defaultOption()
	mixOptions(opt, opts...)
	return &Visitor{
		opts: opt,
	}
}

type Visitor struct {
	opts *visitOption
}

// VisitFolder implements IFolderVisitor.
func (v *Visitor) VisitFolder(path string, cb VisitCallback, opts ...Option) error {
	if len(opts) > 0 {
		mixOptions(v.opts, opts...)
	}
	return v.walkPath(path, cb)
}

func (v *Visitor) VisitFolders(paths []string, cb VisitCallback, opts ...Option) error {
	if len(opts) > 0 {
		mixOptions(v.opts, opts...)
	}
	for _, path := range paths {
		err := v.walkPath(path, cb)
		if err != nil {
			return err
		}
	}
	return nil
}

func (v *Visitor) walkPath(rootPath string, cb VisitCallback) error {
	return utils.SymWalk(rootPath, func(path string, info fs.FileInfo, err error) error {
		if info.IsDir() {
			return v.visitAsFolder(rootPath, path, info.Name(), cb)
		}

		if err != nil {
			return err
		}

		if !v.fileInRule(info) {
			return nil
		}

		err = v.visitFile(rootPath, path, info, cb)
		if err != nil {
			return err
		}

		return nil
	})
}

func (v *Visitor) fileInRule(f fs.FileInfo) bool {
	return v.opts.filePathInRule(f.Name())
}

func (v *Visitor) visitFile(rootPath, path string, f fs.FileInfo, fb VisitCallback) error {
	file := NewFileInfo(path, f)
	file.RootPath = rootPath
	return fb(path, *file)
}

func (v *Visitor) visitAsFolder(rootPath, path string, name string, cb VisitCallback) error {
	file := FileInfo{
		RootPath: rootPath,
		Path:     path,
		Name:     name,
		IsDir:    true,
	}

	return cb(path, file)
}
